package imap;


# require "syslog.pl";


$AF_INET = 2;
$SOCK_STREAM = 1;

$tag = "A000";


sub init
{
	($messageCB, $mailboxCB,$existsCB, $recentCB, $expungeCB, $flagsCB,
	 $searchCB, $fetchCB) = @_;

	$SIG{'HUP'} = \&imap'sighup;
	$SIG{'INT'} = \&imap'sigint;
	$SIG{'ALRM'} = \&imap'sigalarm;

	return 1;
}


sub open
{
local($host, $port) = @_;
local($fh);

	&msg'debug("imap\'open $host $port");

	# &openlog("imap", 'pid', 'mail');

	$sockaddr = 'S n a4 x8';

	# chop($hostname = `hostname`);

	($name, $aliases, $proto) = getprotobyname('tcp');
	($name, $aliases, $port) = getservbyname($port, 'tcp')
										unless $port =~ /^\d+$/;
	# ($name, $aliases, $type, $len, $localaddr) = gethostbyname($hostname);
	($name, $aliases, $type, $len, $serveraddr) = gethostbyname($host);

	# $localsock = pack($sockaddr, $AF_INET, 0, $localaddr);
	$serversock = pack($sockaddr, $AF_INET, $port, $serveraddr);

	# ($a, $b, $c, $d) = unpack('C4', $localaddr);
	($p, $q, $r, $s) = unpack('C4', $serveraddr);
	&msg'debug("connecting from $a.$b.$c.$d to port $port at $p.$q.$r.$s");

	if (!socket(S, $AF_INET, $SOCK_STREAM, $proto))
	{
		&msg'error("can not open socket");
		return 0;
	}

	# if (!bind(S, $localsock))
	# {
	# 	&msg'error("can not bind socket");
	# 	return 0;
	# }

	if (!connect(S, $serversock))
	{
		&msg'error("can not connect");
		return 0;
	}

	$fh = select(S); $| = 1; select($fh);

	return 1;
}

sub close
{
	&msg'debug("imap\'close");

	# &closelog();

	close(S);
}


sub noop
{
	&msg'debug("imap\'noop");

	return &send("NOOP");
}


sub login
{
local($user) = @_;

	&msg'debug("imap\'login $user");

	system "stty -echo </dev/tty >/dev/tty";
	print STDERR "password: ";
	chop($passwd = <STDIN>);
	print STDERR "\n";
	system "stty echo </dev/tty >/dev/tty";

	return &send("LOGIN $user $passwd");
}

sub logout
{
	&msg'debug("imap\'logout");

	return &send("LOGOUT");
}


sub select
{
	local($mailbox) = @_;

	&msg'debug("imap\'select $mailbox");

	return &send("SELECT $mailbox");
}


sub find
{
local($pattern) = @_;

	&msg'debug("imap\'find $pattern");

	return &send("FIND MAILBOXES $pattern");
}


sub create
{
local($mailbox) = @_;

	&msg'debug("imap\'create $mailbox");

	return &send("CREATE $mailbox");
}


sub delete
{
local($mailbox) = @_;

	&msg'debug("imap\'delete $mailbox");

	return &send("DELETE $mailbox");
}


sub subscribe
{
local($mailbox) = @_;

	&msg'debug("imap\'subscribe $mailbox");

	return &send("SUBSCRIBE MAILBOX $mailbox");
}


sub unsubscribe
{
local($mailbox) = @_;

	&msg'debug("imap\'unsubscribe $mailbox");

	return &send("UNSUBSCRIBE MAILBOX $mailbox");
}


sub check
{
	&msg'debug("imap\'check");

	return &send("CHECK");
}


sub expunge
{
	&msg'debug("imap\'expunge");

	return &send("EXPUNGE");
}


sub fetch
{
local($sequence, $data) = @_;

	&msg'debug("imap\'fetch $sequence $data");

	return &send("FETCH $sequence $data");
}


sub search
{
local($criteria) = @_;

	&msg'debug("imap\'search $criteria");

	return &send("SEARCH $criteria");
}


sub store
{
local($sequence, $data, $value) = @_;

	&msg'debug("imap\'store $sequence $data $value");

	return &send("STORE $sequence $data $value");
}


sub puts
{
local($line) = @_;

	print S "$line\n";
}


sub send
{
local($line) = @_;

	&msg'debug("imap\'send $line");

	&puts("$tag $line");

	return $tag++;
}


sub gets
{
local($count, $timeout) = @_;
local($n, $line);

	alarm($timeout);

	if ($count == 0)
	{
		$/ = "\015\012";
		$line = <S>;
		$n = length($line);
		$/ = "\n";
	}
	else
	{
		$n = read(S, $line, $count);
	}

	alarm(0);

	return ($n, $line);
}


sub recv
{
local($timeout) = @_;
local($line, $i, $n, $len, @data);

	($n, $data[0]) = &gets(0, $timeout);
	$line = $data[0];

	for ($i = 0; $line =~ /\{([0-9]+)\}\015\012$/m; )
	{
		for ($len = $1, $i++; $len > 0; $i++)
		{
			($n, $data[$i]) = &gets($len, $timeout);
			$len = $len - $n;
			$line = $data[$i];
		}
		if ($len == 0)
		{
			($n, $data[$i]) = &gets(0, $timeout);
			$line = $data[$i];
		}
	}

	return join("", @data);
}


sub loop
{
local($match, $timeout) = @_;
local($tag, $result, $message);

	&msg'debug("imap\'loop $match");

	while (1)
	{
		($tag, $result, $message) = &imap'handle(&recv($timeout));

		if ($tag eq $match)
		{
			return ($tag, $result, $message);
		}
	}

	return "";
}

sub handle
{
local($data) = @_;
local($tag, $result, $command, $message, $remainder);

	($tag, $result, $data) = split(' ', $data, 3);
	($command, $remainder) = split(' ', $data, 2);
	($message, $remainder) = split(/\015\012/, $data, 2);

	&msg'debug("imap\'handle $tag $result $message");

	RESULT:
	{
	$result =~ /[0-9]+/ && do
		{
			COMMAND:
			{
			$command =~ /EXISTS/i && do
				{
					eval { &$existsCB($tag, $result); };
					&msg'debug($@) if $@;
					last COMMAND;
				};
			$command =~ /RECENT/i && do
				{
					eval { &$recentCB($tag, $result); };
					&msg'debug($@) if $@;
					last COMMAND;
				};
			$command =~ /EXPUNGE/i && do
				{
					eval { &$expungeCB($tag, $result); };
					&msg'debug($@) if $@;
					last COMMAND;
				};
			$command =~ /FETCH/i && do
				{
					eval { &$fetchCB($tag, $result, $data); };
					&msg'debug($@) if $@;
					last COMMAND;
				};
			}
			last RESULT;
		};
	$result =~ /FLAGS/i && do
		{
			eval { &$flagsCB($tag, $data); };
			&msg'debug($@) if $@;
			last RESULT;
		};
	$result =~ /SEARCH/i && do
		{
			eval { &$searchCB($tag, $data); };
			&msg'debug($@) if $@;
			last RESULT;
		};
	$result =~ /MAILBOX/i && do
		{
			eval { &$mailboxCB($tag, $data); };
			&msg'debug($@) if $@;
			last RESULT;
		};
	($result =~ /BYE/i ||
	$result =~ /OK/i ||
	$result =~ /NO/i ||
	$result =~ /BAD/i) && do
		{
			eval { &$messageCB($tag, $result, $message); };
			&msg'debug($@) if $@;
			last RESULT;
		};
	}

	return ($tag, $result, $message);
}


sub sighup
{
	&msg'error("exit by hangup");
	exit 2;
}


sub sigint
{
	&msg'error("exit by interrupt");
	&close();
	exit 1;
}


sub sigalarm
{
	&msg'error("exit by alarm");
	exit 3;
}

1;
